#ifndef __SIMPLE_MQTT_HPP
#define __SIMPLE_MQTT_HPP

#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <random>
#include <thread>

extern "C"
{
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #include <sys/select.h>
}

namespace SimpleMQTT
{
    const uint8_t CTRL_MESSAGE_CONNECT      = 1U;
    const uint8_t CTRL_MESSAGE_CONNACK      = 2U;
    const uint8_t CTRL_MESSAGE_PUBLISH      = 3U;
    const uint8_t CTRL_MESSAGE_PUBACK       = 4U;
    const uint8_t CTRL_MESSAGE_PUBREC       = 5U;
    const uint8_t CTRL_MESSAGE_PUBREL       = 6U;
    const uint8_t CTRL_MESSAGE_PUBCOMP      = 7U;
    const uint8_t CTRL_MESSAGE_SUBSCRIBE    = 8U;
    const uint8_t CTRL_MESSAGE_SUBACK       = 9U;
    const uint8_t CTRL_MESSAGE_UNSUBSCRIBE  = 10U;
    const uint8_t CTRL_MESSAGE_UNSUBACK     = 11U;
    const uint8_t CTRL_MESSAGE_PINGREQ      = 12U;
    const uint8_t CTRL_MESSAGE_PINGRESP     = 13U;
    const uint8_t CTRL_MESSAGE_DISCONNECT   = 14U;
    const uint8_t CONNECT_USER_NAME_FLAG    = (1<<7U);
    const uint8_t CONNECT_PASSWORD_FLAG     = (1<<6U);
    const uint8_t CONNECT_WILL_RETAIN_FLAG  = (1<<5U);
    const uint8_t CONNECT_WILL_QOS0_FLAG    = (0<<3U);
    const uint8_t CONNECT_WILL_QOS1_FLAG    = (1<<3U);
    const uint8_t CONNECT_WILL_QOS2_FLAG    = (2<<3U);
    const uint8_t CONNECT_WILL_FLAG         = (1<<2U);
    const uint8_t CONNECT_CLEAN_FLAG        = (1<<1U);
    
    typedef enum
    {
        RET_UNIMPLEMENTED       =  1,
        RET_OK                  =  0,
        RET_TIMEOUT             = -1,
        RET_CONN_NOT_OPENED     = -2,
        RET_SOCKET_ERROR        = -3,
        RET_MALLOC_ERROR        = -4,
        RET_MESSAGE_ERROR       = -5,
        RET_SERVER_UNSUPPORTED  = -6,
        RET_SERVER_UNAVAILABLE  = -7,
        RET_SERVER_UNAUTHORIZED = -8
    }   ReturnCodes;
    typedef enum
    {
        QOS_LEVEL_0,
        QOS_LEVEL_1,
        QOS_LEVEL_2,
    }   QoSLevels;

    class MQTTClient
    {
        private:
            void *buffer;
            int socketFd;
            bool connected;
            uint16_t bufferSize;
            const char *clientID;
            const char *clientAddr;
            uint16_t    clientPort;
        public:
            MQTTClient(){};
            ~MQTTClient();
            ReturnCodes init(
                const char *id,
                const char *addr,
                uint16_t port,
                uint16_t bufferSize);
            void deinit();
            ReturnCodes startConnection(
                const char *addr,
                uint16_t port,
                uint8_t flags,
                uint16_t keepalive,
                const char *willTopic,
                const char *willMsg,
                const char *username,
                const char *password,
                uint32_t timeout
                );
            void endConnection();
            ReturnCodes waitConnectAck(uint32_t timeout);
            int ping();
            ReturnCodes publish(
                const char *topic,
                uint8_t *payloadData,
                uint16_t payloadLength,
                QoSLevels qosLevel,
                bool retain
                );
            int subscribe();
    };
} // namespace SimpleMQTT

#endif